package com.googlecode.chios.jnet.io;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.chios.jnet.buffer.ChannelBuffer;
import com.googlecode.chios.jnet.handler.ConnectTask;
import com.googlecode.chios.jnet.handler.ReadTask;
import com.googlecode.chios.jnet.handler.WriteTask;
import com.googlecode.chios.jnet.worker.AioWorker;

/**
 * 
 * 
 * @author luzj
 *
 */
public class AioSession extends Session {
	
	private Logger logger = LoggerFactory.getLogger(AioSession.class);
	
	private AsynchronousSocketChannel channel;
	
	private AioWorker worker;
	
	private ByteBuffer readBufer;
	
	private ChannelBuffer outBuffer;

	private ReadCompletionHandler readCompletionHandler = new ReadCompletionHandler();
	
	private boolean isOpen;
	
	private long lastActiveTimeStamp;
	
	public AioSession(AsynchronousSocketChannel channel, Configration configration) {
		super(configration);
		this.channel = channel;
		this.readBufer = ByteBuffer.allocate(configration.SO_RCVBUFF);
		this.outBuffer = configration.bufferFactory.getBuffer(configration.readBuffSize);
		this.isOpen = true;
	}

	public void start() {
		worker.offerTask(new ConnectTask(this));
		peddingRead();
	}

	public void write(byte[] bytes) {
		worker.offerTask(new WriteTask(this, bytes));
	}
	
	private void peddingRead() {
		channel.read(readBufer, this, this.readCompletionHandler);
	}

	public void workWith(AioWorker worker) {
		this.worker = worker;
	}
	
	public SocketAddress remoteSocketAddress() {
		try {
			return this.channel.getRemoteAddress();
		} catch (IOException e) {
			logger.debug("none remote address ?", e);
		}
		return null;
	}
	
	public void onException(Throwable exc) {
		//
	}
	
	public AsynchronousSocketChannel channel() {
		return this.channel;
	}

	public void close() {
		try {
			this.isOpen = false;
			this.channel.close();
		} catch (IOException e) {
			logger.debug(e.getMessage(), e.getCause());
		}
	}
	
	public boolean isOpen() {
		return this.isOpen;
	}
	
	public void updateTimeStamp() {
		this.lastActiveTimeStamp = System.currentTimeMillis();
	}
	
	class ReadCompletionHandler implements CompletionHandler<Integer, AioSession> {

		private Logger logger = LoggerFactory.getLogger(ReadCompletionHandler.class);
		
		@Override
		public void completed(Integer readSize, AioSession session) {
			logger.debug("session ["+ session.remoteSocketAddress() +"] read " + readSize + " bytes");
			if(readSize < 0) {
				session.close();
				return;
			}
			
			try {
				session.updateTimeStamp();
				session.readBufer.flip();
				
				session.outBuffer.writeBytes(session.readBufer);
				session.readBufer.clear();
				
				worker.offerTask(new ReadTask(session, session.outBuffer));
			} finally {
				session.peddingRead();
			}
			
		}

		@Override
		public void failed(Throwable exc, AioSession session) {
			logger.debug("read failed", exc.getMessage(), exc.getCause());
			session.onException(exc);
			session.close();
		}
		
	}

}
